<?php


namespace app\facade;

use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Address\PayToPubKeyHashAddress;
use BitWasp\Bitcoin\Network\NetworkFactory;

use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\Factory\Signer;
use BitWasp\Bitcoin\transaction\TransactionOutput;
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;

use app\btctool\Credential;

class Btc
{

    public function txP2pkh()
    {

        $privFactory = new PrivateKeyFactory;
        $priv = $privFactory->fromWif('L4S7r7ZptLRzqZmB4hmQVv2ivjBhxEqL7Qe1ELAUR3Uw7QfNtmjV');
        $publicKey = $priv->getPublicKey();
        $pubKeyHash = $publicKey->getPubKeyHash();

        echo 'privateKey ' . $priv->toWif() . PHP_EOL;

        echo 'publicKey ' . $publicKey->getHex() . PHP_EOL;

        echo 'hash ' . $pubKeyHash->getHex() . PHP_EOL;
        $p2pkh = new PayToPubKeyHashAddress($priv->getPubKeyHash());

        // $network = NetworkFactory::bitcoinRegtest(); // 私链地址
        $network = NetworkFactory::bitcoinTestnet(); // 测试地址
        // $network = NetworkFactory::bitcoin();           // 主链地址
        echo 'p2pkh ' . $p2pkh->getAddress($network) . PHP_EOL;

        // 转账方
        $spender = new Credential($priv);
        $shop = Credential::new();
        $network = NetworkFactory::bitcoinRegtest();
        echo "shop address " . $shop->p2pkh->getAddress($network) . PHP_EOL;

        $change = Credential::new();

        $utxo = $this->fund($spender, 6);
        print_r($utxo);

        $rawtx = $this->spend($utxo, $spender, $shop, 5, $change);
        //echo $rawtx . PHP_EOL;
        //decode($rawtx);
        $txid = $this->confirm($rawtx);
        echo 'txid => ' . $txid . PHP_EOL;
    }


    function fund($spender, $amount)
    {
        $addr = $spender->p2pkh->getAddress(NetworkFactory::bitcoinRegtest());
        // $client = new Client('http://user:123456@127.0.0.1:18443');
        $txid = $this->client->exec('sendtoaddress', $addr, $amount)->result;
        // $this->client->exec('generate',6);
        $tx = $this->client->exec('getrawtransaction', $txid, true)->result;
        foreach ($tx->vout as $txo) {
            if (in_array($addr, $txo->scriptPubKey->addresses))
                return ['txid' => $txid, 'vout' => $txo->n, 'value' => $txo->value];
        }
    }


    function spend($utxo, $spender, $shop, $amount, $change)
    {
        $tval = $amount * 100000000; //in satoshi
        $fee = 0.0001 * 100000000;
        var_dump($fee);
        $cval = intval($utxo['value'] * 100000000 - $tval - $fee);
        if ($cval < 0) throw new Exception('utxo not enough!');
        $tx = TransactionFactory::build()
            ->input($utxo['txid'], $utxo['vout'])
            ->output($tval, $shop->p2pkh->getScriptPubKey())
            ->output($cval, $change->p2pkh->getScriptPubKey())
            ->get();

        $signer = new Signer($tx);
        $lock = new TransactionOutput(0, $spender->p2pkh->getScriptPubKey());
        $signed = $signer->sign(0, $spender->prv, $lock)
            ->get();

        $serializer = new TransactionSerializer();
        $raw = $serializer->serialize($signed);

        return $raw->getHex();
    }

    function decode($rawtx)
    {
        $client = new Client('http://user:123456@127.0.0.1:18443');
        $ret = $client->exec('decoderawtransaction', $rawtx);
        var_dump($ret);
    }

    function confirm($rawtx)
    {
        $txid = $this->client->exec('sendrawtransaction', $rawtx)->result;
        // $this->client->exec('generate',6);
        return $txid;
    }

    public function getbalance($address = '')
    {
        $opts = [
            'json' => [
                'jsonrpc' => '1.0',
                'method' => 'getbalance',
                'params' => [$address],
                'id' => time()
            ]
        ];
        return $this->post($opts);
    }


    public function create($type = 3)
    {
        $data = [];

        $prv = PrivateKeyFactory::create(true);

        $data['privateKey'] = $prv->toWif();
        $data['publicKey'] = $prv->getPublicKey()->getHex();
        $data['hash'] = $prv->getPubKeyHash()->getHex();

        $p2pkh = new PayToPubKeyHashAddress($prv->getPubKeyHash());
        switch ($type) {
            case 1:
                $network = NetworkFactory::bitcoinRegtest(); // 私链地址
                break;
            case 2:
                $network = NetworkFactory::bitcoinTestnet(); // 测试地址
                break;
            case 3:
                $network = NetworkFactory::bitcoin();        // 主链地址
                break;
            default:
                $network = NetworkFactory::bitcoinRegtest(); // 私链地址
                break;
        }
        $data['p2pkh'] = $p2pkh->getAddress($network);
        return $data;
    }
}
